Skip to content

feat(connectors): database selector + per-card write toggle (#568) - #633

Merged
alfredo1996 merged 10 commits into
release/2.0from
feat/issue-568-db-selector-write-toggle
May 1, 2026
Merged

feat(connectors): database selector + per-card write toggle (#568)#633
alfredo1996 merged 10 commits into
release/2.0from
feat/issue-568-db-selector-write-toggle

Conversation

@alfredo1996

@alfredo1996 alfredo1996 commented May 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • API routes to enumerate available databases from saved connections
  • Neo4j and PostgreSQL connectors for database/schema discovery
  • Widget editor database dropdown and optional write toggle
  • Per-card write authorization enforced server-side

Recreated from #631 to fix CI trigger after base branch change.

Closes #568

Test plan

  • Unit tests for all new API routes
  • Unit tests for database-selector component
  • Unit tests for query route database override paths
  • Unit tests for write route widget-connection validation
  • Unit tests for listDatabases/listSchemas in query-executor
  • Unit tests for widget-editor-store database/allowWrites fields
  • E2E tests pass locally (199 passed)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Per-card database selection: widgets can now override their connection's default database when the connection supports this capability.
    • Widget-level write permissions: individually enable or disable write access on widgets, with authorization enforced at both user and widget levels.
    • Database and schema enumeration: discover available databases and schemas when configuring widgets and their connections.

@coderabbitai

coderabbitai Bot commented May 1, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

This PR implements per-card database selection and write-permission toggles. It adds listDatabases/listSchemas methods to connection modules, introduces API endpoints for database discovery, implements a database selector UI component, and enforces widget-level write permissions server-side alongside user-level canWrite checks.

Changes

Cohort / File(s) Summary
Connection Module Abstractions
connection/src/generalized/ConnectionModule.ts, connection/src/neo4j/Neo4jConnectionModule.ts, connection/src/postgresql/PostgresConnectionModule.ts
Added abstract listDatabases() method to ConnectionModule and implemented for Neo4j (queries system db with SHOW DATABASES, filters offline/system) and Postgres (queries pg_database, excludes templates). Postgres also implements listSchemas() (queries information_schema.schemata, excludes pg_*). All methods return empty arrays on failure instead of throwing.
Database Listing API Routes
app/src/app/api/connections/[id]/databases/route.ts, app/src/app/api/connections/list-databases-inline/route.ts
Added two new endpoints: GET /api/connections/[id]/databases retrieves databases for a saved connection; POST /api/connections/list-databases-inline handles inline requests during connection creation. Both decrypt credentials, invoke listDatabases/listSchemas, and return empty arrays on failure.
Query Executor Helpers
app/src/lib/query/query-executor.ts
Exported listDatabases() and listSchemas() functions that delegate to connection module methods and guard schema listing with runtime type-checking.
Write Query Authorization
app/src/app/api/query/write/route.ts, app/src/app/api/query/write/__tests__/route.test.ts
Updated write endpoint to accept optional widgetId/dashboardId, load widget from dashboard, enforce widget.allowWrites === true, and apply widget-level database overrides when connection permits. Tests validate 403 rejection when allowWrites is false, widget mismatch, and correct database override behavior. Existing legacy flows (no widget context) still work.
Query Endpoint Database Overrides
app/src/app/api/query/route.ts, app/src/app/api/query/__tests__/route.test.ts
Added optional database field to request validation; conditionally replaces credentials' database when both override is provided and connection allows per-card selection. Tests cover all combinations of allowPerCardDb flag and override presence.
Connection Response Fields
app/src/app/api/connections/route.ts
Extended GET and POST responses to include allowPerCardDb flag and updatedAt timestamp.
Database Selector Component
app/src/components/widget-editor/database-selector.tsx, app/src/components/widget-editor/__tests__/database-selector.test.tsx
New React component that fetches databases via useConnectionDatabases hook, renders dropdown with "Connection default" sentinel option (__default__), and invokes callback with selected name or empty string for default.
Widget Editor Modal
app/src/components/widget-editor-modal.tsx
Added canWrite prop, conditional "Enable write mode" checkbox (only when canWrite === true), and database selector rendering (when connection allowPerCardDb and widget is queryable).
Widget Editor State
app/src/stores/widget-editor-store.ts, app/src/stores/__tests__/widget-editor-store.test.ts
Added database and allowWrites fields; setConnectionId clears database override; loadFromWidget hydrates both from saved widget state with defaults ("" and false).
Widget Save Builder
app/src/components/widget-editor/use-widget-save.ts
Includes database and allowWrites in saved DashboardWidget; omits for content-only widgets.
Hooks for Database & Query
app/src/hooks/use-connection-databases.ts, app/src/hooks/use-connection-databases.test.ts, app/src/hooks/use-connections.ts, app/src/hooks/use-query-execution.ts, app/src/hooks/use-widget-query.ts
New useConnectionDatabases hook queries /api/connections/{connectionId}/databases with caching; ConnectionListItem includes allowPerCardDb flag; QueryInput/WidgetQueryInput now carry optional database field; React Query queryKey includes database for cache separation.
Database Schema
app/src/lib/db/schema.ts
Added connections.allowPerCardDb column (defaults true); extended DashboardWidget interface with optional database and allowWrites fields.
Dashboard Edit Page
app/src/app/(dashboard)/[id]/edit/page.tsx
Passed canWrite prop (from session.user.canWrite or true fallback) to WidgetEditorModal.
Card Container
app/src/components/card-container.tsx
Includes widget database property in queryInput passed to useWidgetQuery.
Connection Tests
connection/__tests__/connection/list-databases.ts, app/src/lib/__tests__/query/query-executor-core.test.ts
New test suites validating listDatabases/listSchemas for Neo4j and Postgres (via testcontainers), graceful error handling, and query-executor delegation. Tests verify filtering (exclude system/template dbs) and empty-array fallback on failure.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Editor as Widget Editor
    participant Store as Widget Store
    participant API as /api/connections/{id}/databases
    participant DBModule as Connection Module
    
    User->>Editor: Select connection in widget editor
    Editor->>Store: setConnectionId(id)
    activate Store
    Store->>Store: Clear database override
    deactivate Store
    
    User->>Editor: View database selector
    Editor->>API: GET /api/connections/{id}/databases
    activate API
    API->>DBModule: listDatabases(credentials)
    DBModule-->>API: [db1, db2, ...]
    API-->>Editor: { data: { databases: [...] } }
    deactivate API
    
    Editor->>Editor: Render dropdown with options
    User->>Editor: Select database
    Editor->>Store: setDatabase(selected)
    activate Store
    Store->>Store: Update database field
    deactivate Store
    
    Editor->>Store: Get database value
    Editor->>Store: Get allowWrites value
    Store-->>Editor: { database, allowWrites }
    Editor->>Editor: Save widget with overrides
Loading
sequenceDiagram
    participant Client as Client/Widget
    participant QueryAPI as POST /api/query/write
    participant DB as Database
    participant Dashboard as Dashboard Store
    
    Client->>QueryAPI: POST { connectionId, query, widgetId, dashboardId }
    activate QueryAPI
    
    QueryAPI->>Dashboard: Load dashboard by dashboardId
    Dashboard-->>QueryAPI: DashboardRow
    
    QueryAPI->>QueryAPI: Find widget in layoutJson
    alt Widget not found or allowWrites false
        QueryAPI-->>Client: 403 Forbidden
    else Widget found and allowWrites true
        QueryAPI->>QueryAPI: Check widget.connectionId matches
        alt Mismatch
            QueryAPI-->>Client: 403 Forbidden
        else Match
            QueryAPI->>QueryAPI: Apply database override if widget.database provided
            QueryAPI->>DB: Execute query with credentials
            DB-->>QueryAPI: Result
            QueryAPI-->>Client: 200 { data }
        end
    end
    deactivate QueryAPI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

enhancement, pkg:app, pkg:connection, area:connectors, area:widgets, testing

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(connectors): database selector + per-card write toggle' directly summarizes the main changes: adding database selection and per-widget write authorization across connectors and the widget editor.
Linked Issues check ✅ Passed All major objectives from issue #568 are met: listDatabases/listSchemas implemented for Neo4j and PostgreSQL connectors [#568], API endpoints GET /api/connections/[id]/databases and POST /api/connections/list-databases-inline created [#568], database selector UI added to widget editor [#568], per-widget allowWrites toggle implemented with server-side enforcement [#568], and comprehensive test coverage added [#568].
Out of Scope Changes check ✅ Passed All changes are tightly scoped to the stated objectives: connector introspection methods, new API routes for database enumeration, widget editor UI for database/write selection, schema updates, and server-side request handling modifications directly support the database selector and per-card write toggle features.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-568-db-selector-write-toggle

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

alfredorubin96 and others added 10 commits May 1, 2026 23:19
Add database introspection to connectors (listDatabases for Neo4j/PG,
listSchemas for PG) with graceful fallback. Introduce per-widget write
mode toggle in Advanced tab and per-card database override in widget
editor. Server enforces allowWrites by looking up the widget in the
dashboard layout before executing write queries.

- connection/: abstract listDatabases(), Neo4j SHOW DATABASES with
  fallback, PG pg_database + information_schema.schemata
- API: GET /connections/[id]/databases, POST /connections/list-databases-inline
- Schema: allow_per_card_db on connections, database + allowWrites on DashboardWidget
- Write route: when widgetId + dashboardId provided, verifies widget.allowWrites;
  legacy form-widget path (no widgetId) still works with user-level canWrite
- Widget editor: database dropdown (Data tab), write mode toggle (Advanced tab)
- 26 new tests (10 connector integration, 11 API route, 5 write enforcement)

Closes #568

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add database to widget-query cache key to prevent cross-DB collisions
- Forward full config (SSL, timeouts) in list-databases-inline route
- Validate widget-connection binding and gate DB override by allowPerCardDb in write route
- Return allowPerCardDb from POST /api/connections
- Show placeholder in DatabaseSelector when no databases available
- Clear database override when switching connections in store
- Exclude information_schema and escape underscore in PG listSchemas
- Hide write mode checkbox for content-only widgets (markdown/iframe)
- Only persist database/allowWrites for non-content-only widgets
- Add 3 tests: per-card DB propagation, DB override gating, widget-connection mismatch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ides

Cover DatabaseSelector component (loading, empty, select, __default__
mapping), useConnectionDatabases hook (query key, enabled/disabled,
fetch endpoint), and per-card database override logic in the query
route (allowPerCardDb true/false/undefined, missing database field).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ase fields

- Add listDatabases tests: returns databases, correct credentials forwarding
- Add listSchemas tests: returns schemas, empty array when unsupported
- Add widget-editor-store tests: database/allowWrites defaults, setters,
  setConnectionId clears database, resetForAdd clears both

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Forward full validated config in list-databases-inline (not partial)
- Strengthen DB override test: assert connection-level database preserved
- Add legacy widget test: missing allowWrites defaults to 403
- Add loadFromWidget hydration tests for database/allowWrites fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add listDatabases/listSchemas to module-level mock factory so
TypeScript accepts the return type when overriding in tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@alfredo1996
alfredo1996 force-pushed the feat/issue-568-db-selector-write-toggle branch from e24c046 to ec9d06c Compare May 1, 2026 21:24
@sonarqubecloud

sonarqubecloud Bot commented May 1, 2026

Copy link
Copy Markdown

@alfredo1996
alfredo1996 merged commit f42125e into release/2.0 May 1, 2026
9 of 13 checks passed
@alfredo1996
alfredo1996 deleted the feat/issue-568-db-selector-write-toggle branch May 16, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants